package org.changs.launcher.device;

import android.os.Handler;

import com.google.gson.JsonObject;

import org.changs.aplug.annotation.ServiceScoped;
import org.changs.aplug.interf.Action1;
import org.changs.servlet.AplugClient;
import org.changs.servlet.CommunicationConfig;
import org.changs.servlet.IOUtils;
import org.changs.servlet.message.NetworkDevice;
import org.changs.servlet.net.NetworkDeviceScanner;
import org.changs.servlet.net.NetworkDeviceScanner1;
import org.changs.servlet.net.NetworkUtils;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;

/**
 * Created by yincs on 2017/9/15.
 */

@ServiceScoped
public class ConnectServletOpt implements Runnable {
    private final NetworkDeviceScanner1 mNetworkDeviceScanner = new NetworkDeviceScanner1(10);
    private final Handler mHandler;

    private ArrayList<String> mLocalIPList;
    private NetworkDevice mNetworkDevice;
    private boolean isScanning;
    private Action1<Boolean> mConnectedAction;
    private Disposable mHeartbeatSubscribe;

    @Inject
    public ConnectServletOpt(Handler handler) {
        this.mHandler = handler;
    }

    @Override
    public void run() {
        if (isScanning) return;
        mHandler.removeCallbacks(this);
        mLocalIPList = NetworkUtils.getInterfacesWithOnlyIp(true, CommunicationConfig.DEFAULT_DISABLED_INTERFACES);
        mNetworkDeviceScanner.setLock(false);
        if (mNetworkDeviceScanner.scan(mLocalIPList, myScannerHandler)) {
            isScanning = true;
        } else {
            Timber.d("扫描失败，3秒后将重新扫描");
            mHandler.postDelayed(this, 3000);
        }
    }

    private void heartbeat() {
        if (mHeartbeatSubscribe != null && !mHeartbeatSubscribe.isDisposed())
            mHeartbeatSubscribe.dispose();

        final JsonObject message = new JsonObject();
        message.addProperty(IOUtils.EXTRA_ACTION, IOUtils.Action.CONNECT);
        message.addProperty(IOUtils.EXTRA_DATA, mLocalIPList.get(0));
        mHeartbeatSubscribe = AplugClient.rxSend(mNetworkDevice.getIp(), CommunicationConfig.PHONE_SERVER_PORT, message)
                .delay(2, TimeUnit.SECONDS)
                .repeatUntil(() -> mNetworkDevice == null)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnComplete(() -> mHeartbeatSubscribe = null)
                .subscribe(response -> {
                    Timber.d("heartbeat success:" + response);
                }, throwable -> {
                    Timber.d("heartbeat error:" + throwable.getMessage());
                    mNetworkDevice = null;
                    if (mConnectedAction != null)
                        mConnectedAction.call(false);
                });
    }

    private NetworkDeviceScanner.ScannerHandler myScannerHandler = new NetworkDeviceScanner.ScannerHandler() {
        @Override
        public void onDeviceFound(InetAddress address) {
            if (mLocalIPList == null || mLocalIPList.size() == 0) {
                Timber.e("onDeviceFound: mLocalIPList is empty");
                return;
            }
            Timber.d("mLocalIPList.get(0) = " + mLocalIPList.get(0));
            final JsonObject message = new JsonObject();
            message.addProperty(IOUtils.EXTRA_ACTION, IOUtils.Action.CONNECT);
            message.addProperty(IOUtils.EXTRA_DATA, mLocalIPList.get(0));
            AplugClient.rxSend(address.getHostAddress(), CommunicationConfig.PHONE_SERVER_PORT, message)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(response -> {
                        Timber.d("response = " + response);
                        mNetworkDevice = new NetworkDevice(address.getHostAddress());
                        mNetworkDeviceScanner.interrupt();
                        if (mConnectedAction != null) {
                            Timber.d("已连接上,当前线程" + Thread.currentThread().getName());
                            mConnectedAction.call(true);
                        }
                    }, throwable -> Timber.d("throwable = " + throwable));
        }

        @Override
        public void onScannerCompleted() {
            Timber.d("onScannerCompleted()");
            isScanning = false;
            if (mNetworkDevice == null) {
                Timber.d("没有发现设备、3秒后将重新扫描");
                mHandler.postDelayed(ConnectServletOpt.this, 3000);
            } else {
                heartbeat();
            }
        }
    };

    public NetworkDevice getNetworkDevice() {
        return mNetworkDevice;
    }

    public void setConnectedAction(Action1<Boolean> action) {
        this.mConnectedAction = action;
    }
}
